﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace AppConfigVerifier
{
	public class GlobalContext
	{
		readonly List<RuleSetCreator> creators = new List<RuleSetCreator>();
		readonly List<Scanner> scanners = new List<Scanner>();

		public delegate void LogDelegate(GlobalContext sender, String content, 
			Location startLocation, Location endLocation, String message);

		public event LogDelegate ErrorReceived;
		public event LogDelegate WarningReceived;

		public GlobalContext(String content)
		{
			if (content == null)
			{
				throw new ArgumentNullException("content");
			}

			this.content = content;
		}

		public void Run()
		{
			string content = this.content;
			IEnumerable<TypeToken> tokens = scanners.SelectMany(
				scanner => scanner.Scan(content)).Distinct(new TokenComparer());

			var globalInfo = new GlobalInfo(this.content);
			globalInfo.Log.ErrorReceived += globalInfo_ErrorReceived;
			globalInfo.Log.WarningReceived += globalInfo_WarningReceived;
			try
			{
				foreach (TypeToken token in tokens)
				{
					var localContext = new LocalContext(token, globalInfo);
					IEnumerable<Rule> ruleSet = creators.SelectMany(
						creator => creator.CreateRules(localContext));
					foreach (Rule rule in ruleSet)
					{
						rule.Apply(localContext);
					}
				}
			}
			finally
			{
				globalInfo.Log.ErrorReceived -= globalInfo_ErrorReceived;
				globalInfo.Log.WarningReceived -= globalInfo_WarningReceived;
			}
		}


		public void globalInfo_ErrorReceived(GlobalInfo sender, 
			Location location, Location endLocation, string message)
		{
			if (ErrorReceived != null)
			{
				ErrorReceived(this, sender.Content, location, endLocation, message);
			}
		}

		public void globalInfo_WarningReceived(GlobalInfo sender, 
			Location location, Location endLocation, string message)
		{
			if (WarningReceived != null)
			{
				WarningReceived(this, sender.Content, location, endLocation, message);
			}
		}
        
		readonly String content;

		public String Content
		{
			get
			{
				return content;
			}
		}

		public void AddRuleSetCreator(RuleSetCreator creator)
		{
			if (creator == null)
			{
				throw new ArgumentNullException("creator");
			}
			creators.Add(creator);
		}

		public void AddScanner(Scanner scanner)
		{
			if (scanner == null)
			{
				throw new ArgumentNullException("scanner");
			}
			scanners.Add(scanner);
		}
	}
}
